/*******************************************************************************
*  file    : filedataprovider.cpp
*  created : 14.07.2014
*  author  : Slyshyk Oleksiy (alexSlyshyk@gmail.com)
*******************************************************************************/

#include "filedataprovider.hpp"
#include <QDir>
#include <QDebug>
#include <QPluginLoader>
#include <QCoreApplication>

#include <algorithm>
#include <boost/range.hpp>
#include <boost/range/algorithm.hpp>


#include <qlogger.hpp>
#include <interfaces.hpp>
#include <dataobject/dataobject.hpp>

namespace rng = boost::range;
namespace
{
QLogger * l = QLoggerEngine::getLogger("Core");
}

FileDataProvider::FileDataProvider(QObject *parent) :
    DataProvider(parent)
{
    watcher_ = new QFileSystemWatcher(this);
    connect(watcher_, &QFileSystemWatcher::directoryChanged, this, &FileDataProvider::onDirectoryChanged );
}

QString FileDataProvider::name() const
{
    return QStringLiteral("FileDataProvider");
}

void FileDataProvider::setFormatPlugins(const QSharedPointer<QMap<QString, ReadFileFormatInterface *> > &plugins)
{
    formatPlugins_ = (plugins);
}

void FileDataProvider::setObjectPlugins(const QSharedPointer<QMap<QString, SaveLoadObjectInterface *> > &plugins)
{
    saveLoadObjectPlugins_ = (plugins);
}

void FileDataProvider::setWatcherPath(const QString &path)
{
    if(watcher_->directories().size())
        watcher_->removePaths(watcher_->directories());
    watcher_->addPath(path);

    QDir dir(path);
    auto files = dir.entryInfoList(QStringList(),QDir::Files,QDir::Time);
    if(files.size())
        lastProcessedDate_ = files.first().lastModified();
}

void FileDataProvider::processExistFiles(const QString &path)
{
    lastProcessedDate_ = QDateTime();
    onDirectoryChanged(path);
}

QString FileDataProvider::path() const
{
    if(watcher_->directories().size())
        return watcher_->directories()[0];

    return QString();
}

void FileDataProvider::onDirectoryChanged(const QString &path)
{
    if(!formatPlugins_)
        return;
    if(!saveLoadObjectPlugins_)
        return;

    QDir dir(path);
    auto files = dir.entryInfoList(QStringList(),QDir::Files,QDir::Time);

    rng::reverse(files);
    auto not_processed = std::upper_bound(files.begin(),files.end(),lastProcessedDate_,
                                          [](const QDateTime& d,const QFileInfo& i1){return d < i1.lastModified();});

    if( not_processed == files.end() )
        return;

    lastProcessedDate_ = files.back().lastModified();
    QStringList formatKeys = formatPlugins_->keys();
    auto saveLoadObjKeys = saveLoadObjectPlugins_->keys();
    for(;not_processed != files.end(); ++not_processed)
        {
            if(not_processed->baseName().contains("pattern") || not_processed->baseName().contains("test"))
                continue;

            auto key = std::find_if(formatKeys.begin(),formatKeys.end(), [&](const QString & k)
                                    {return (*formatPlugins_)[k]->formats().contains(not_processed->suffix());} );
            if(key != formatKeys.end())
                {
                    if((*formatPlugins_)[(*key)]->read(not_processed->absoluteFilePath()))
                        {
                            NamedDataPtr d = NamedDataPtr(new NamedData);
                            d->name = not_processed->baseName();
                            d->sampleRate = (*formatPlugins_)[(*key)]->sampleRate();
                            d->data = (*formatPlugins_)[(*key)]->getData().toStdVector();
                            (*formatPlugins_)[(*key)]->flush();

                            emit dataReady(count_, d);
                            count_++;
                            qApp->processEvents(QEventLoop::AllEvents/*, 10*/);
                        }
                }
            auto sl_key = rng::find_if(saveLoadObjKeys,
                                       [this,&not_processed](const QString& k)
                                       {return (*saveLoadObjectPlugins_)[k]->formats().contains(not_processed->suffix());});
            if(sl_key != saveLoadObjKeys.end())
                {
                    QSharedPointer<DataObject> data_obj = loadDataObject((*not_processed));
                    if(data_obj)
                        {
                            count_++ ;
                            emit dataObjectReady(data_obj);
                        }
                }
            qApp->processEvents(QEventLoop::AllEvents/*, 50*/);
        }
}

QSharedPointer<DataObject> FileDataProvider::loadDataObject(const QString& file_name)
{
    QFileInfo file_info(file_name);
    return loadDataObject(file_info);
}

QSharedPointer<DataObject> FileDataProvider::loadDataObject(const QFileInfo &file_info)
{
    QSharedPointer<DataObject> data_obj;
    auto saveLoadObjKeys = saveLoadObjectPlugins_->keys();
    auto sl_key = rng::find_if(saveLoadObjKeys,
                               [this,file_info](const QString& k)
                               {return (*saveLoadObjectPlugins_)[k]->formats().contains(file_info.suffix());});
    if(sl_key != saveLoadObjKeys.end())
        {
            data_obj = QSharedPointer<DataObject>(static_cast<DataObject*>((*saveLoadObjectPlugins_)[(*sl_key)]
                                                  ->loadObjectFromFile(file_info.absoluteFilePath())));
            data_obj->dataL()->name = file_info.baseName();
            data_obj->dataR()->name = file_info.baseName();

            if(data_obj->time().toMSecsSinceEpoch() < 1000) // простейшая проверка на корректность
                {
                    QString date_string = file_info.baseName();
                    date_string.replace("do_auto_","");
                    QDateTime dt = QDateTime::fromString(date_string,"MM_dd_hh_mm_ss");
                    if(dt.isValid())
                        {
                            QDateTime f_dt = file_info.lastModified();
                            int y = f_dt.date().year() - dt.date().year();
                            dt = dt.addYears(y);
                            data_obj->setTime(dt);
                        }
                    else
                        {
                            logError(l) << file_info.absoluteFilePath() << " Can't read data object";
                        }
                }
        }
    return data_obj;
}

int FileDataProvider::filesQuantity(const QString &dir_name)
{
    QDir dir(dir_name);
    auto files = dir.entryInfoList(QStringList(),QDir::Files,QDir::Time);
    return files.size();
}

